package com.earthchen.mattress.netty;

import com.earthchen.mattress.dao.MattressDao;
import com.earthchen.mattress.domain.MattressData;
import com.earthchen.mattress.dto.SerializeObject;
import com.earthchen.mattress.mqtt.SendMsgThread;
import com.earthchen.mattress.mqtt.ServerMQTT;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.msgpack.jackson.dataformat.MessagePackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 处理数据包
 *
 * @author: EarthChen
 * @date: 2018/04/26
 */
public class SocketByteHandler extends ChannelInboundHandlerAdapter {

    private static Logger logger = LoggerFactory.getLogger(SocketByteHandler.class);

    private static ExecutorService executorService = Executors.newFixedThreadPool(10);

    private ServerMQTT server;

    public SocketByteHandler() throws MqttException {
        super();
        server = new ServerMQTT("/World");
        server.setMessage(new MqttMessage());
        // 保证消息能到达一次
        server.getMessage().setQos(1);
        server.getMessage().setRetained(true);
    }

    /**
     * 需要处理所有接收到的数据，所以你重写了 channelRead()
     * 方法。在这个服务器应用程序中，你将数据简单地回送给了远程节点。
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        logger.info("服务端收到数据---{}", msg);

        ByteBuf result = (ByteBuf) msg;
        byte[] data = new byte[result.readableBytes()];
        // msg中存储的是ByteBuf类型的数据，把数据读取到byte[]中

        result.readBytes(data);

        save(data);

        // 释放资源，这行很关键
        result.release();
        String response = "success";
        // 在当前场景下，发送的数据必须转换成ByteBuf数组
        ByteBuf encoded = ctx.alloc().buffer(4 * response.length());
        encoded.writeBytes(response.getBytes());
        ctx.write(encoded);
        ctx.flush();
    }


    /**
     * 解包
     *
     * @param dd
     * @throws IOException
     */
    private MattressData unSerialize(byte[] dd) throws IOException {
        logger.info("解析数据包--{}", dd);
        ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());

        SerializeObject msg = objectMapper.readValue(dd, SerializeObject.class);
        logger.info("解析数据包成功，反序列化的对象为{}", msg);

        // 给床垫对象赋值
        MattressData mattressData = new MattressData();
        mattressData.setSn(msg.getSn());
        mattressData.setFv((Integer) msg.getD().get("fv"));
        mattressData.setState((String) msg.getD().get("st"));
        mattressData.setHeartbeat((Integer) msg.getD().get("hb"));
        mattressData.setBreath((Integer) msg.getD().get("br"));
        mattressData.setWet((Boolean) msg.getD().get("wt"));
        mattressData.setOdor((Integer) msg.getD().get("od"));
        mattressData.setWeight((Integer) msg.getD().get("we"));
        List<Integer> list = (List<Integer>) msg.getD().get("p");
        Integer[] array = new Integer[list.size()];
        mattressData.setPosition(list.toArray(array));
        mattressData.setCreateTime(new Date());
        logger.info("床垫数据赋值成功{}", mattressData);
        return mattressData;
    }

    /**
     * 允许你对 Throwable 的任何子类型做出反应， 在这里你
     * 记录了异常并关闭了连接。虽然一个更加完善的应用程序也许会尝试从异常中恢复，但在这个场
     * 景下，只是通过简单地关闭连接来通知远程节点发生了错误。
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.error("发生错误并关闭连接");
        cause.printStackTrace();
        ctx.close();
    }

    /**
     * 保存数据
     *
     * @param res tcp数据包
     * @throws IOException
     */
    public void save(byte[] res) throws IOException {
        byte[] data = new byte[res.length - 4];
        System.arraycopy(res, 4, data, 0, data.length);
        // 解包
        MattressData mattressData = unSerialize(data);
        // 提交任务发送消息
        executorService.execute(new SendMsgThread(server, "/World", mattressData));
//        MattressDao mattressDao = new MattressDao();

//        boolean success = mattressDao.saveMattressData(mattressData);
//        if (success) {
//            logger.info("保存床垫数据成功");
//        } else {
//            logger.error("报错床垫数据失败");
//        }
    }
}
